// -*- mode:c++ -*-

// Copyright (c) 2011-2013, 2016-2024 Arm Limited
// All rights reserved
//
// The license below extends only to copyright in the software and shall
// not be construed as granting a license to any other intellectual
// property including but not limited to intellectual property relating
// to a hardware implementation of the functionality of the software
// licensed hereunder.  You may use the software subject to the license
// terms below provided that you ensure that this notice is replicated
// unmodified and in its entirety in all distributions of the software,
// modified or unmodified, in source code or in binary form.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met: redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer;
// redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution;
// neither the name of the copyright holders nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

let {{

    header_output = ""
    decoder_output = ""
    exec_output = ""

    def createCcCode64(carry, overflow):
        code = ""
        code += '''
            uint16_t _iz, _in;
            _in = bits(resTemp, intWidth - 1);
            _iz = ((resTemp & mask(intWidth)) == 0);
            CondCodesNZ = (_in << 1) | _iz;
            DPRINTF(Arm, "(in, iz) = (%d, %d)\\n", _in, _iz);
        '''
        if overflow and overflow != "none":
            code +=  '''
                uint16_t _iv;
                _iv = %s & 1;
                CondCodesV = _iv;
                DPRINTF(Arm, "(iv) = (%%d)\\n", _iv);
            ''' % overflow
        if carry and carry != "none":
            code += '''
                uint16_t _ic;
                _ic = %s & 1;
                CondCodesC = _ic;
                DPRINTF(Arm, "(ic) = (%%d)\\n", _ic);
            ''' % carry
        return code

    oldC = 'CondCodesC'
    oldV = 'CondCodesV'
    # Dicts of ways to set the carry flag.
    carryCode64 = {
        "none": "none",
        "add": 'findCarry(intWidth, resTemp, Op164, secOp)',
        "sub": 'findCarry(intWidth, resTemp, Op164, ~secOp)',
        "logic": '0'
    }
    # Dict of ways to set the overflow flag.
    overflowCode64 = {
        "none": "none",
        "add": 'findOverflow(intWidth, resTemp, Op164, secOp)',
        "sub": 'findOverflow(intWidth, resTemp, Op164, ~secOp)',
        "logic": '0'
    }

    immOp2 = "[[maybe_unused]] uint64_t secOp = imm;"
    sRegOp2 = "[[maybe_unused]] uint64_t secOp = " + \
              "shiftReg64(Op264, shiftAmt, shiftType, intWidth);"
    eRegOp2 = "[[maybe_unused]] uint64_t secOp = " + \
              "extendReg64(Op264, extendType, shiftAmt, intWidth);"

    def buildDataWork(mnem, code, flagType, suffix, buildCc, buildNonCc,
                      base, templateBase):
        code = '''
        [[maybe_unused]] uint64_t resTemp = 0;
        ''' + code
        ccCode = createCcCode64(carryCode64[flagType], overflowCode64[flagType])
        Name = mnem.capitalize() + suffix
        iop = ArmInstObjParams(mnem, Name, base, code)
        iopCc = ArmInstObjParams(mnem + "s", Name + "Cc", base, code + ccCode)

        def subst(iop):
            global header_output, decoder_output, exec_output
            header_output += eval(templateBase + "Declare").subst(iop)
            decoder_output += eval(templateBase + "Constructor").subst(iop)
            exec_output += BasicExecute.subst(iop)

        if buildNonCc:
            subst(iop)
        if buildCc:
            subst(iopCc)

    def buildXImmDataInst(mnem, code, flagType = "logic", \
                          buildCc = True, buildNonCc = True, \
                          suffix = "XImm"):
        buildDataWork(mnem, immOp2 + code, flagType, suffix,
                      buildCc, buildNonCc, "DataXImmOp", "DataXImm")

    def buildXSRegDataInst(mnem, code, flagType = "logic", \
                           buildCc = True, buildNonCc = True, \
                           suffix = "XSReg"):
        buildDataWork(mnem, sRegOp2 + code, flagType, suffix,
                      buildCc, buildNonCc, "DataXSRegOp", "DataXSReg")

    def buildXERegDataInst(mnem, code, flagType = "logic", \
                           buildCc = True, buildNonCc = True, \
                           suffix = "XEReg"):
        buildDataWork(mnem, eRegOp2 + code, flagType, suffix,
                      buildCc, buildNonCc, "DataXERegOp", "DataXEReg")

    def buildDataInst(mnem, code, flagType = "logic",
                      buildCc = True, buildNonCc = True):
        buildXImmDataInst(mnem, code, flagType, buildCc, buildNonCc)
        buildXSRegDataInst(mnem, code, flagType, buildCc, buildNonCc)
        buildXERegDataInst(mnem, code, flagType, buildCc, buildNonCc)

    buildXImmDataInst("adr", "Dest64 = RawPC + imm", buildCc = False);
    buildXImmDataInst("adrp", "Dest64 = (RawPC & ~mask(12)) + imm",
                      buildCc = False);
    buildDataInst("and", "Dest64 = resTemp = Op164 & secOp;")
    buildDataInst("eor", "Dest64 = Op164 ^ secOp;", buildCc = False)
    buildXSRegDataInst("eon", "Dest64 = Op164 ^ ~secOp;", buildCc = False)
    buildDataInst("sub", "Dest64 = resTemp = Op164 - secOp;", "sub")
    buildDataInst("add", "Dest64 = resTemp = Op164 + secOp;", "add")
    buildXSRegDataInst("adc",
            "Dest64 = resTemp = Op164 + secOp + %s;" % oldC, "add")
    buildXSRegDataInst("sbc",
            "Dest64 = resTemp = Op164 - secOp - !%s;" % oldC, "sub")
    buildDataInst("orr", "Dest64 = Op164 | secOp;", buildCc = False)
    buildXSRegDataInst("orn", "Dest64 = Op164 | ~secOp;", buildCc = False)
    buildXSRegDataInst("bic", "Dest64 = resTemp = Op164 & ~secOp;")

    def buildDataXImmInst(mnem, code, optArgs = []):
        global header_output, decoder_output, exec_output
        classNamePrefix = mnem[0].upper() + mnem[1:]
        templateBase = "DataXImm"
        iop = ArmInstObjParams(mnem, classNamePrefix + "64",
                               templateBase + "Op", code, optArgs)
        header_output += eval(templateBase + "Declare").subst(iop)
        decoder_output += eval(templateBase + "Constructor").subst(iop)
        exec_output += BasicExecute.subst(iop)

    def buildDataXRegInst(mnem, regOps, code, optArgs = [],
                          overrideOpClass=None):
        global header_output, decoder_output, exec_output
        templateBase = "DataX%dReg" % regOps
        classNamePrefix = mnem[0].upper() + mnem[1:]
        if overrideOpClass:
            iop = ArmInstObjParams(mnem, classNamePrefix + "64",
                                   templateBase + "Op",
                                   { 'code': code,
                                     'op_class': overrideOpClass },
                                   optArgs)
        else:
            iop = ArmInstObjParams(mnem, classNamePrefix + "64",
                                   templateBase + "Op", code, optArgs)
        header_output += eval(templateBase + "Declare").subst(iop)
        decoder_output += eval(templateBase + "Constructor").subst(iop)
        exec_output += BasicExecute.subst(iop)

    buildDataXRegInst("madd", 3, "Dest64 = Op164 + Op264 * Op364",
        overrideOpClass="IntMultOp")
    buildDataXRegInst("msub", 3, "Dest64 = Op164 - Op264 * Op364",
        overrideOpClass="IntMultOp")
    buildDataXRegInst("smaddl", 3,
        "XDest = XOp1 + sext<32>(WOp2) * sext<32>(WOp3)",
        overrideOpClass="IntMultOp")
    buildDataXRegInst("smsubl", 3,
        "XDest = XOp1 - sext<32>(WOp2) * sext<32>(WOp3)",
        overrideOpClass="IntMultOp")
    buildDataXRegInst("smulh", 2, '''
        uint64_t op1H = (int32_t)(XOp1 >> 32);
        uint64_t op1L = (uint32_t)XOp1;
        uint64_t op2H = (int32_t)(XOp2 >> 32);
        uint64_t op2L = (uint32_t)XOp2;
        uint64_t mid1 = ((op1L * op2L) >> 32) + op1H * op2L;
        uint64_t mid2 = op1L * op2H;
        uint64_t result = ((uint64_t)(uint32_t)mid1 + (uint32_t)mid2) >> 32;
        result += shiftReg64(mid1, 32, ASR, intWidth);
        result += shiftReg64(mid2, 32, ASR, intWidth);
        XDest = result + op1H * op2H;
    ''', overrideOpClass="IntMultOp")
    buildDataXRegInst("umaddl", 3, "XDest = XOp1 + WOp2 * WOp3",
        overrideOpClass="IntMultOp")
    buildDataXRegInst("umsubl", 3, "XDest = XOp1 - WOp2 * WOp3",
        overrideOpClass="IntMultOp")
    buildDataXRegInst("umulh", 2, '''
        uint64_t op1H = (uint32_t)(XOp1 >> 32);
        uint64_t op1L = (uint32_t)XOp1;
        uint64_t op2H = (uint32_t)(XOp2 >> 32);
        uint64_t op2L = (uint32_t)XOp2;
        uint64_t mid1 = ((op1L * op2L) >> 32) + op1H * op2L;
        uint64_t mid2 = op1L * op2H;
        uint64_t result = ((uint64_t)(uint32_t)mid1 + (uint32_t)mid2) >> 32;
        result += mid1 >> 32;
        result += mid2 >> 32;
        XDest = result + op1H * op2H;
    ''', overrideOpClass="IntMultOp")

    buildDataXRegInst("asrv", 2,
        "Dest64 = shiftReg64(Op164, Op264, ASR, intWidth)")
    buildDataXRegInst("lslv", 2,
        "Dest64 = shiftReg64(Op164, Op264, LSL, intWidth)")
    buildDataXRegInst("lsrv", 2,
        "Dest64 = shiftReg64(Op164, Op264, LSR, intWidth)")
    buildDataXRegInst("rorv", 2,
        "Dest64 = shiftReg64(Op164, Op264, ROR, intWidth)")

    crcCode = '''
    constexpr uint8_t size_bytes = %(sz)d;
    constexpr uint32_t poly = %(polynom)s;

    // Initial value is often a previously evaluated
    // crc value hence is always 32bit in CRC32
    uint32_t initial_crc = Op164 & 0xFFFFFFFF;

    uint64_t data = htole(Op264);
    auto data_buffer = reinterpret_cast<uint8_t*>(&data);

    Dest = crc32<poly>(
        data_buffer,   /* Message register */
        initial_crc,   /* Initial value of the CRC */
        size_bytes     /* Size of the original Message */
    );
    '''
    buildDataXRegInst("crc32b", 2,
        crcCode % {"sz": 1, "polynom": "0x04C11DB7"})
    buildDataXRegInst("crc32h", 2,
        crcCode % {"sz": 2, "polynom": "0x04C11DB7"})
    buildDataXRegInst("crc32w", 2,
        crcCode % {"sz": 4, "polynom": "0x04C11DB7"})
    buildDataXRegInst("crc32x", 2,
        crcCode % {"sz": 8, "polynom": "0x04C11DB7"})

    buildDataXRegInst("crc32cb", 2,
        crcCode % {"sz": 1, "polynom": "0x1EDC6F41"})
    buildDataXRegInst("crc32ch", 2,
        crcCode % {"sz": 2, "polynom": "0x1EDC6F41"})
    buildDataXRegInst("crc32cw", 2,
        crcCode % {"sz": 4, "polynom": "0x1EDC6F41"})
    buildDataXRegInst("crc32cx", 2,
        crcCode % {"sz": 8, "polynom": "0x1EDC6F41"})

    buildDataXRegInst("sdiv", 2, '''
        int64_t op1 = Op164;
        int64_t op2 = Op264;
        if (intWidth == 32) {
            op1 = sext<32>(op1);
            op2 = sext<32>(op2);
        }
        Dest64 = op2 == -1 ? -op1 : op2 ? op1 / op2 : 0;
    ''', overrideOpClass="IntDivOp")
    buildDataXRegInst("udiv", 2, "Dest64 = Op264 ? Op164 / Op264 : 0",
        overrideOpClass="IntDivOp")

    buildDataXRegInst("cls", 1, '''
        uint64_t op1 = Op164;
        if (bits(op1, intWidth - 1))
            op1 ^= mask(intWidth);
        Dest64 = (op1 == 0) ? intWidth - 1 : (intWidth - 2 - findMsbSet(op1));
    ''')
    buildDataXRegInst("clz", 1, '''
        Dest64 = (Op164 == 0) ? intWidth : (intWidth - 1 - findMsbSet(Op164));
    ''')
    buildDataXRegInst("rbit", 1, '''
        Dest64 = reverseBits(Op164, intWidth/8);
    ''')
    buildDataXRegInst("rev", 1, '''
        if (intWidth == 32)
            Dest64 = betole<uint32_t>(Op164);
        else
            Dest64 = betole<uint64_t>(Op164);
    ''')
    buildDataXRegInst("rev16", 1, '''
        int count = intWidth / 16;
        uint64_t result = 0;
        for (unsigned i = 0; i < count; i++) {
            uint16_t hw = Op164 >> (i * 16);
            result |= (uint64_t)betole<uint16_t>(hw) << (i * 16);
        }
        Dest64 = result;
    ''')
    buildDataXRegInst("rev32", 1, '''
        int count = intWidth / 32;
        uint64_t result = 0;
        for (unsigned i = 0; i < count; i++) {
            uint32_t hw = Op164 >> (i * 32);
            result |= (uint64_t)betole<uint32_t>(hw) << (i * 32);
        }
        Dest64 = result;
    ''')

    msrMrs64EnabledCheckCode = '''
        auto pre_flat = (MiscRegIndex)snsBankedIndex64(%s, xc->tcBase());
        auto *isa = static_cast<ArmISA::ISA *>(xc->tcBase()->getIsaPtr());
        auto flat_idx = (MiscRegIndex)isa->flattenMiscIndex(pre_flat);

        // Check for read/write access right
        if (fault = checkFaultAccessAArch64SysReg(flat_idx, Cpsr,
            xc->tcBase(), *this); fault != NoFault) {
            return fault;
        }
    '''

    msr_check_code = msrMrs64EnabledCheckCode % 'dest'
    mrs_check_code = msrMrs64EnabledCheckCode % 'op1'

    mrsCode = mrs_check_code + '''
        XDest = MiscOp1_ud;
    '''
    mrsIop = ArmInstObjParams("mrs", "Mrs64", "RegMiscRegImmOp64",
                              mrsCode,
                              ["IsSerializeBefore"])
    header_output += RegMiscRegOp64Declare.subst(mrsIop)
    decoder_output += RegMiscRegOp64Constructor.subst(mrsIop)
    exec_output += BasicExecute.subst(mrsIop)

    mrsNZCVCode = '''
        CPSR cpsr = 0;
        cpsr.nz = CondCodesNZ;
        cpsr.c = CondCodesC;
        cpsr.v = CondCodesV;
        XDest = cpsr;
    '''
    mrsNZCViop = ArmInstObjParams("mrs", "MrsNZCV64",
                                  "RegMiscRegImmOp64", mrsNZCVCode)
    header_output += RegMiscRegOp64Declare.subst(mrsNZCViop)
    decoder_output += RegMiscRegOp64Constructor.subst(mrsNZCViop)
    exec_output += BasicExecute.subst(mrsNZCViop)

    msrCode = msr_check_code + '''
        MiscDest_ud = XOp1;
    '''
    msrIop = ArmInstObjParams("msr", "Msr64", "MiscRegRegImmOp64",
                              msrCode,
                              ["IsSerializeAfter", "IsNonSpeculative"])
    header_output += MiscRegRegOp64Declare.subst(msrIop)
    decoder_output += MiscRegRegOp64Constructor.subst(msrIop)
    exec_output += BasicExecute.subst(msrIop)

    tlbiCode = msr_check_code + '''
        performTlbi(xc, flat_idx, XOp1);
    '''
    dvmCode = '''
    if (%(dvmCheck)s) {
        Request::Flags memAccessFlags =
            Request::STRICT_ORDER | Request::TLBI;

        fault = xc->initiateMemMgmtCmd(memAccessFlags);

        PendingDvm = true;
    }
    '''
    msrTlbiIop = ArmInstObjParams("msr", "Tlbi64LocalHub", "TlbiOp64",
        {
            "code" : tlbiCode,
            "dvm_code" : dvmCode %
            {
                "dvmCheck" : "HCR hcr = Hcr64; hcr.fb && dvmEnabled"
            }
        },
        ["IsSerializeAfter", "IsNonSpeculative"])
    header_output += DvmTlbiDeclare.subst(msrTlbiIop)
    decoder_output += DvmTlbiConstructor.subst(msrTlbiIop)
    exec_output += BasicExecute.subst(msrTlbiIop)
    exec_output += DvmInitiateAcc.subst(msrTlbiIop)
    exec_output += DvmCompleteAcc.subst(msrTlbiIop)

    msrTlbiSIop = ArmInstObjParams("msr", "Tlbi64ShareableHub", "TlbiOp64",
        {
            "code" : tlbiCode,
            "dvm_code" : dvmCode %
            {
                "dvmCheck" : "dvmEnabled"
            }
        },
        ["IsSerializeAfter", "IsNonSpeculative"])
    header_output += DvmTlbiDeclare.subst(msrTlbiSIop)
    decoder_output += DvmTlbiConstructor.subst(msrTlbiSIop)
    exec_output += BasicExecute.subst(msrTlbiSIop)
    exec_output += DvmInitiateAcc.subst(msrTlbiSIop)
    exec_output += DvmCompleteAcc.subst(msrTlbiSIop)

    atCode = msr_check_code + '''
        uint64_t par_result;
        std::tie(fault, par_result) = performAt(xc, flat_idx, XOp1);
        MiscDest_ud = XOp1;
        MiscDest2_ud = par_result;
    '''
    msrAtIop = ArmInstObjParams("msr", "At64Hub", "AtOp64",
        {
            "code" : atCode
        })
    header_output += AtDeclare.subst(msrAtIop)
    decoder_output += AtConstructor.subst(msrAtIop)
    exec_output += BasicExecute.subst(msrAtIop)

    msrNZCVCode = '''
        CPSR cpsr = XOp1;
        CondCodesNZ = cpsr.nz;
        CondCodesC = cpsr.c;
        CondCodesV = cpsr.v;
    '''
    msrNZCVIop = ArmInstObjParams("msr", "MsrNZCV64", "MiscRegRegImmOp64",
                                  msrNZCVCode)
    header_output += MiscRegRegOp64Declare.subst(msrNZCVIop)
    decoder_output += MiscRegRegOp64Constructor.subst(msrNZCVIop)
    exec_output += BasicExecute.subst(msrNZCVIop)


    msrdczva_ea_code = msr_check_code
    msrdczva_ea_code += '''
           Request::Flags memAccessFlags = Request::CACHE_BLOCK_ZERO;
           EA = XBase;
           assert(!(Dczid & 0x10));
           uint64_t op_size = 1ULL << (Dczid + 2);
           EA &= ~(op_size - 1);

   '''

    msrDCZVAIop = ArmInstObjParams("dc zva", "Dczva", "SysDC64",
                                   { "ea_code" : msrdczva_ea_code,
                                     "memacc_code" : ';',
                                     "use_uops" : 0,
                                     "op_wb" : ";",
                                     "fa_code" : ";"},
                                   ['IsStore']);
    header_output += DCStore64Declare.subst(msrDCZVAIop);
    decoder_output += DCStore64Constructor.subst(msrDCZVAIop);
    exec_output += DCStore64Execute.subst(msrDCZVAIop);
    exec_output += DCStore64InitiateAcc.subst(msrDCZVAIop);
    exec_output += Store64CompleteAcc.subst(msrDCZVAIop);

    # Cache maintenance fault annotation
    # The DC ZVA instruction is not classified as a cache maintenance
    # instruction, and therefore we shouldn't annotate it.
    cachem_fa = '''
        fault->annotate(ArmISA::ArmFault::CM, 1);
        fault->annotate(ArmISA::ArmFault::OFA, faultAddr);
    '''

    msrdccvau_ea_code = msr_check_code
    msrdccvau_ea_code += '''
           Request::Flags memAccessFlags = Request::CLEAN | Request::DST_POU;
           EA = XBase;
           faultAddr = EA;
           System *sys = xc->tcBase()->getSystemPtr();
           Addr op_size = sys->cacheLineSize();
           EA &= ~(op_size - 1);
    '''

    msrDCCVAUIop = ArmInstObjParams("dc cvau", "Dccvau", "SysDC64",
                                    { "ea_code" : msrdccvau_ea_code,
                                      "memacc_code" : ';',
                                      "use_uops" : 0,
                                      "op_wb" : ";",
                                      "fa_code" : cachem_fa},
                                    ['IsStore']);
    header_output += DCStore64Declare.subst(msrDCCVAUIop);
    decoder_output += DCStore64Constructor.subst(msrDCCVAUIop);
    exec_output += DCStore64Execute.subst(msrDCCVAUIop);
    exec_output += DCStore64InitiateAcc.subst(msrDCCVAUIop);
    exec_output += Store64CompleteAcc.subst(msrDCCVAUIop);


    msrdccvac_ea_code = msr_check_code
    msrdccvac_ea_code += '''
           Request::Flags memAccessFlags = Request::CLEAN | Request::DST_POC;
           EA = XBase;
           faultAddr = EA;
           System *sys = xc->tcBase()->getSystemPtr();
           Addr op_size = sys->cacheLineSize();
           EA &= ~(op_size - 1);
    '''

    msrDCCVACIop = ArmInstObjParams("dc cvac", "Dccvac", "SysDC64",
                                    { "ea_code" : msrdccvac_ea_code,
                                      "memacc_code" : ';',
                                      "use_uops" : 0,
                                      "op_wb" : ";",
                                      "fa_code" : cachem_fa},
                                    ['IsStore']);
    header_output += DCStore64Declare.subst(msrDCCVACIop);
    decoder_output += DCStore64Constructor.subst(msrDCCVACIop);
    exec_output += DCStore64Execute.subst(msrDCCVACIop);
    exec_output += DCStore64InitiateAcc.subst(msrDCCVACIop);
    exec_output += Store64CompleteAcc.subst(msrDCCVACIop);


    msrdccivac_ea_code = msr_check_code
    msrdccivac_ea_code += '''
           Request::Flags memAccessFlags = Request::CLEAN |
              Request::INVALIDATE | Request::DST_POC;
           EA = XBase;
           faultAddr = EA;
           System *sys = xc->tcBase()->getSystemPtr();
           Addr op_size = sys->cacheLineSize();
           EA &= ~(op_size - 1);
    '''

    msrDCCIVACIop = ArmInstObjParams("dc civac", "Dccivac", "SysDC64",
                                     { "ea_code" : msrdccivac_ea_code,
                                       "memacc_code" : ';',
                                       "use_uops" : 0,
                                       "op_wb" : ";",
                                       "fa_code" : cachem_fa},
                                     ['IsStore']);
    header_output += DCStore64Declare.subst(msrDCCIVACIop);
    decoder_output += DCStore64Constructor.subst(msrDCCIVACIop);
    exec_output += DCStore64Execute.subst(msrDCCIVACIop);
    exec_output += DCStore64InitiateAcc.subst(msrDCCIVACIop);
    exec_output += Store64CompleteAcc.subst(msrDCCIVACIop);


    msrdcivac_ea_code = msr_check_code
    msrdcivac_ea_code += '''
           Request::Flags memAccessFlags = Request::INVALIDATE |
              Request::DST_POC;
           EA = XBase;
           faultAddr = EA;
           HCR hcr = Hcr64;
           CPSR cpsr = Cpsr;
           ExceptionLevel el = (ExceptionLevel) (uint8_t) cpsr.el;
           if (el == EL1 && EL2Enabled(xc->tcBase()) && hcr.vm) {
               memAccessFlags = memAccessFlags | Request::CLEAN;
           }
           System *sys = xc->tcBase()->getSystemPtr();
           Addr op_size = sys->cacheLineSize();
           EA &= ~(op_size - 1);
    '''

    msrDCIVACIop = ArmInstObjParams("dc ivac", "Dcivac", "SysDC64",
                                    { "ea_code" : msrdcivac_ea_code,
                                      "memacc_code" : ';',
                                      "use_uops" : 0,
                                      "op_wb" : ";",
                                      "fa_code" : cachem_fa},
                                    ['IsStore']);
    header_output += DCStore64Declare.subst(msrDCIVACIop);
    decoder_output += DCStore64Constructor.subst(msrDCIVACIop);
    exec_output += DCStore64Execute.subst(msrDCIVACIop);
    exec_output += DCStore64InitiateAcc.subst(msrDCIVACIop);
    exec_output += Store64CompleteAcc.subst(msrDCIVACIop);

    def buildMsrImmInst(mnem, inst_name, code):
        global header_output, decoder_output, exec_output
        msrImmPermission = '''
            auto pre_flat =
                (MiscRegIndex)snsBankedIndex64(dest, xc->tcBase());
            auto *isa = static_cast<ArmISA::ISA *>(xc->tcBase()->getIsaPtr());
            auto misc_index = (MiscRegIndex)isa->flattenMiscIndex(pre_flat);

            if (fault = checkFaultAccessAArch64SysReg(misc_index,
                Cpsr, xc->tcBase(), *this); fault != NoFault) {
                    return fault;
            }

        '''
        msrIop = ArmInstObjParams("msr", inst_name, "MiscRegImmOp64",
                                  msrImmPermission + code,
                                  ["IsSerializeAfter", "IsNonSpeculative"])
        header_output += MiscRegOp64Declare.subst(msrIop)
        decoder_output += MiscRegOp64Constructor.subst(msrIop)
        exec_output += BasicExecute.subst(msrIop)

    buildMsrImmInst("msr", "MsrImm64", '''
        // Mask and shift immediate (depending on PSTATE field)
        // before assignment
        MiscDest_ud = miscRegImm();
    ''')

    buildMsrImmInst("msr", "MsrImmDAIFSet64", '''
        CPSR cpsr = Cpsr;
        cpsr.daif = cpsr.daif | imm;
        Cpsr = cpsr;
    ''')

    buildMsrImmInst("msr", "MsrImmDAIFClr64", '''
        CPSR cpsr = Cpsr;
        cpsr.daif = cpsr.daif & ~imm;
        Cpsr = cpsr;
    ''')

    def buildDataXCompInst(mnem, instType, suffix, code):
        global header_output, decoder_output, exec_output
        templateBase = "DataXCond%s" % instType
        iop = ArmInstObjParams(mnem, mnem.capitalize() + suffix + "64",
                               templateBase + "Op", code)
        header_output += eval(templateBase + "Declare").subst(iop)
        decoder_output += eval(templateBase + "Constructor").subst(iop)
        exec_output += BasicExecute.subst(iop)

    def buildDataXCondImmInst(mnem, code):
        buildDataXCompInst(mnem, "CompImm", "Imm", code)
    def buildDataXCondRegInst(mnem, code):
        buildDataXCompInst(mnem, "CompReg", "Reg", code)
    def buildDataXCondSelInst(mnem, code):
        buildDataXCompInst(mnem, "Sel", "", code)

    def condCompCode(flagType, op, imm):
        ccCode = createCcCode64(carryCode64[flagType], overflowCode64[flagType])
        opDecl = "[[maybe_unused]] uint64_t secOp = imm;"
        if not imm:
            opDecl = "[[maybe_unused]] uint64_t secOp = Op264;"
        return opDecl + '''
            if (testPredicate(CondCodesNZ, CondCodesC, CondCodesV, condCode)) {
                uint64_t resTemp = Op164 ''' + op + ''' secOp;
        ''' + ccCode + '''
            } else {
                CondCodesNZ = (defCc >> 2) & 0x3;
                CondCodesC = (defCc >> 1) & 0x1;
                CondCodesV = defCc & 0x1;
            }
        '''

    buildDataXCondImmInst("ccmn", condCompCode("add", "+", True))
    buildDataXCondImmInst("ccmp", condCompCode("sub", "-", True))
    buildDataXCondRegInst("ccmn", condCompCode("add", "+", False))
    buildDataXCondRegInst("ccmp", condCompCode("sub", "-", False))

    condSelCode = '''
        if (testPredicate(CondCodesNZ, CondCodesC, CondCodesV, condCode)) {
            Dest64 = Op164;
        } else {
            Dest64 = %(altVal)s;
        }
    '''
    buildDataXCondSelInst("csel", condSelCode % {"altVal" : "Op264"})
    buildDataXCondSelInst("csinc", condSelCode % {"altVal" : "Op264 + 1"})
    buildDataXCondSelInst("csinv", condSelCode % {"altVal" : "~Op264"})
    buildDataXCondSelInst("csneg", condSelCode % {"altVal" : "-Op264"})
}};
